package com.authine.cloudpivot.ext.utils.premission;

import com.alibaba.cola.dto.RequestContext;
import com.alibaba.cola.dto.SingleResponse;
import com.alibaba.fastjson.JSON;
import com.authine.cloudpivot.data.CriteriaQuery;
import com.authine.cloudpivot.data.Criterion;
import com.authine.cloudpivot.data.support.CriteriaQueryImpl;
import com.authine.cloudpivot.data.support.SqlWithParams;
import com.authine.mvp.app.launcher.config.PropertiesConfig;
import com.authine.mvp.app.launcher.domain.utills.ApplicationContextUtils;
import com.authine.mvp.app.launcher.dto.bo.query.expression.*;
import com.authine.mvp.app.launcher.utils.BOUtils;
import com.authine.mvp.app.launcher.utils.ExpressionUtils;
import com.authine.mvp.app.permission.constant.RowOperatEnum;
import com.authine.mvp.app.permission.dto.PermDataModelQry;
import com.authine.mvp.app.permission.dto.clientobject.*;
import com.authine.mvp.app.permission.feign.PermFeignServiceI;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.CollectionUtils;

import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;

@Slf4j
public class PermissionUtils {

    public static SqlWithParams permissionSql(String domainCode, String schemaCode, String tablePrefix) {
        PermFeignServiceI permFeignServiceI = ApplicationContextUtils.getBean(PermFeignServiceI.class);
        PropertiesConfig propertiesConfig = ApplicationContextUtils.getBean(PropertiesConfig.class);
        DomainModelDto domainModelDto = new DomainModelDto();
        domainModelDto.setDomainCode(domainCode);
        domainModelDto.setModelCode(schemaCode);
        PermDataModelQry permDataModelQry = new PermDataModelQry();
        permDataModelQry.setAppCode(propertiesConfig.getAppCode());
        permDataModelQry.setUserId(RequestContext.getCorpLoginId());
        permDataModelQry.setBaseUserId(RequestContext.getCorpId());
        permDataModelQry.setUserName(RequestContext.getLoginName());
        permDataModelQry.setDomainModelDtoList(Collections.singletonList(domainModelDto));
        SingleResponse<PermDataModelDto> response = permFeignServiceI.queryPermDataModels(permDataModelQry);
        if (!response.isSuccess()) {
            return null;
        }
        PermDataModelDto permData = response.getData();
        Map<String, ModelDataFeignDto> modelDataDtoMap = permData.getModelDataDtoMap();
        ModelDataFeignDto modelDataFeignDto = modelDataDtoMap.get(schemaCode);
        if (modelDataFeignDto == null) {
            return null;
        }
        Condition condition = getComplexCondition(modelDataFeignDto.getModelDataRowDtoList());
        ExpressionUtils expressionUtils = ApplicationContextUtils.getBean(ExpressionUtils.class);
        Criterion criterion = expressionUtils.condition2Criterion(condition, schemaCode);
        WhereTranslator whereTranslator = new WhereTranslator(tablePrefix);
        CriteriaQuery criteriaQuery = CriteriaQueryImpl.builder().criterion(criterion).build();
        return whereTranslator.translate(criteriaQuery);
    }

    private static Condition getComplexCondition(List<ModelDataRowDto> modelDataRowDtoList) {
        if (CollectionUtils.isEmpty(modelDataRowDtoList)) {
            return null;
        }
        int size = modelDataRowDtoList.size();
        if (size == 1) {
            for (ModelDataRowDto modelDataRowDto : modelDataRowDtoList) {
                List<DataRowDto> dataRowDtoList = modelDataRowDto.getDataRowDtos();
                if (!CollectionUtils.isEmpty(dataRowDtoList)) {
                    if (dataRowDtoList.size() == 1) {
                        for (DataRowDto dataRowDto : dataRowDtoList) {
                            return getSimpleCondition(dataRowDto);
                        }
                    } else if (dataRowDtoList.size() > 1) {
                        ComplexCondition complexCondition = new ComplexCondition();
                        complexCondition.setNature(getNature(modelDataRowDto.getCondition()));
                        for (DataRowDto dataRowDto : dataRowDtoList) {
                            Condition condition = getSimpleCondition(dataRowDto);
                            complexCondition.getConditions().add(condition);
                        }
                        return complexCondition;
                    }
                }
            }
        } else {
            ComplexCondition complexCondition = new ComplexCondition();
            complexCondition.setNature(Nature.OR);
            for (ModelDataRowDto modelDataRowDto : modelDataRowDtoList) {
                List<DataRowDto> dataRowDtoList = modelDataRowDto.getDataRowDtos();
                if (!CollectionUtils.isEmpty(dataRowDtoList)) {
                    if (dataRowDtoList.size() == 1) {
                        for (DataRowDto dataRowDto : dataRowDtoList) {
                            complexCondition.getConditions().add(getSimpleCondition(dataRowDto));
                        }
                    } else if (dataRowDtoList.size() > 1) {
                        ComplexCondition cc = new ComplexCondition();
                        cc.setNature(getNature(modelDataRowDto.getCondition()));
                        for (DataRowDto dataRowDto : dataRowDtoList) {
                            Condition condition = getSimpleCondition(dataRowDto);
                            cc.getConditions().add(condition);
                        }
                        complexCondition.getConditions().add(cc);
                    }
                }
            }
            return complexCondition;
        }
        return null;
    }

    private static Condition getSimpleCondition(DataRowDto dataRowDto) {
        SimpleCondition condition = new SimpleCondition();
        String colName = dataRowDto.getColName();
        condition.setField(colName);
        final Operator operator = getOperator(dataRowDto.getLogicOperta());
        condition.setOperator(operator);
        if (BOUtils.isSystemDeptCode(colName) || BOUtils.isSystemUserCode(colName)) {
            condition.setValue(JSON.toJSONString(dataRowDto.getSubjectList()));
        } else {
            condition.setValue(dataRowDto.getValue());
        }
        if (operator == Operator.NOT_EQ || operator == Operator.NOT_LIKE || operator == Operator.NOT_IN) {
            ComplexCondition complexCondition = new ComplexCondition();
            complexCondition.setNature(Nature.OR);
            SimpleCondition isNullCondition = new SimpleCondition(colName, Operator.IS_NULL, null);
            complexCondition.getConditions().add(condition);
            complexCondition.getConditions().add(isNullCondition);
            return complexCondition;
        }
        return condition;
    }

    private static Operator getOperator(String op) {
        if (Objects.equals(op, RowOperatEnum.lt.getCode())) {
            return Operator.LT;
        } else if (Objects.equals(op, RowOperatEnum.gt.getCode())) {
            return Operator.GT;
        } else if (Objects.equals(op, RowOperatEnum.le.getCode())) {
            return Operator.LTE;
        } else if (Objects.equals(op, RowOperatEnum.ge.getCode())) {
            return Operator.GTE;
        } else if (Objects.equals(op, RowOperatEnum.equals.getCode())) {
            return Operator.EQ;
        } else if (Objects.equals(op, RowOperatEnum.notEquals.getCode())) {
            return Operator.NOT_EQ;
        } else if (Objects.equals(op, RowOperatEnum.like.getCode())) {
            return Operator.LIKE;
        } else if (Objects.equals(op, RowOperatEnum.in.getCode())) {
            return Operator.IN;
        } else if (Objects.equals(op, RowOperatEnum.notIn.getCode())) {
            return Operator.NOT_IN;
        } else if (Objects.equals(op, RowOperatEnum.notLike.getCode())) {
            return Operator.NOT_LIKE;
        } else if (Objects.equals(op, RowOperatEnum.isNull.getCode())) {
            return Operator.IS_NULL;
        } else if (Objects.equals(op, RowOperatEnum.isNotNull.getCode())) {
            return Operator.IS_NOT_NULL;
        }
        return Operator.EQ;
    }

    private static Nature getNature(String operate) {
        if (Objects.equals(operate, "and")) {
            return Nature.AND;
        } else if (Objects.equals(operate, "or")) {
            return Nature.OR;
        }
        return null;
    }
}
